(function(undefined) {
	if( !('ace' in window) ) window['ace'] = {}
	if( !('helper' in window['ace']) ) window['ace'].helper = {}
	if( !('alert' in window['ace']) ) window['ace'].alert = {}
	if( !('match' in window['ace']) ) window['ace'].match = {}
	if( !('form' in window['ace']) ) window['ace'].form = {}
	if( !('vars' in window['ace']) ) window['ace'].vars = {}
	window['ace'].vars['icon'] = ' ace-icon ';
	window['ace'].vars['.icon'] = '.ace-icon';

	ace.vars['touch']	= ('ontouchstart' in window);//(('ontouchstart' in document.documentElement) || (window.DocumentTouch && document instanceof DocumentTouch));
	
	//sometimes the only good way to work around browser's pecularities is to detect them using user-agents
	//though it's not accurate
	var agent = navigator.userAgent
	ace.vars['webkit'] = !!agent.match(/AppleWebKit/i)
	ace.vars['safari'] = !!agent.match(/Safari/i) && !agent.match(/Chrome/i);
	ace.vars['android'] = ace.vars['safari'] && !!agent.match(/Android/i)
	ace.vars['ios_safari'] = !!agent.match(/OS ([4-9])(_\d)+ like Mac OS X/i) && !agent.match(/CriOS/i)
	
	ace.vars['ie'] = window.navigator.msPointerEnabled || (document.all && document.querySelector);//8-11
	ace.vars['old_ie'] = document.all && !document.addEventListener;//8 and below
	ace.vars['firefox'] = 'MozAppearance' in document.documentElement.style;
	
	ace.vars['non_auto_fixed'] = ace.vars['android'] || ace.vars['ios_safari'];
	
	//sometimes we try to use 'tap' event instead of 'click' if jquery mobile plugin is available
	ace['click_event'] = ace.vars['touch'] && jQuery.fn.tap ? 'tap' : 'click';
})();

//some ace helper functions
(function($$ , undefined) {//$$ is ace.helper
 	$$.unCamelCase = function(str) {
		return str.replace(/([a-z])([A-Z])/g, function(match, c1, c2){ return c1+'-'+c2.toLowerCase() })
 	}
 	$$.strToVal = function(str) {
		var res = str.match(/^(?:(true)|(false)|(null)|(\-?[\d]+(?:\.[\d]+)?)|(\[.*\]|\{.*\}))$/i);

		var val = str;
		if(res) {
			if(res[1]) val = true;
			else if(res[2]) val = false;
			else if(res[3]) val = null;	
			else if(res[4]) val = parseFloat(str);
			else if(res[5]) {
				try { val = JSON.parse(str) }
				catch (err) {}
			}
		}

		return val;
 	}
 	$$.getAttrSettings = function(elem, attr_list, prefix) {
		if(!elem) return;
		var list_type = attr_list instanceof Array ? 1 : 2;
		//attr_list can be Array or Object(key/value)
		var prefix = prefix ? prefix.replace(/([^\-])$/ , '$1-') : '';
		prefix = 'data-' + prefix;

		var settings = {}
		for(var li in attr_list) if(attr_list.hasOwnProperty(li)) {
			var name = list_type == 1 ? attr_list[li] : li;
			var attr_val, attr_name = $$.unCamelCase(name.replace(/[^A-Za-z0-9]{1,}/g , '-')).toLowerCase()

			if( ! ((attr_val = elem.getAttribute(prefix + attr_name))  ) ) continue;
			settings[name] = $$.strToVal(attr_val);
		}

		return settings;
 	}

 	$$.scrollTop = function() {
		return document.scrollTop || document.documentElement.scrollTop || document.body.scrollTop
 	}
 	$$.winHeight = function() {
		return window.innerHeight || document.documentElement.clientHeight;
 	}
 	$$.redraw = function(elem, force) {
		if(!elem) return;
		var saved_val = elem.style['display'];
		elem.style.display = 'none';
		elem.offsetHeight;
		if(force !== true) {
			elem.style.display = saved_val;
		}
		else {
			setTimeout(function() {
				elem.style.display = saved_val;
			}, 10);
		}
 	}

 	$$.renderSwitch = function (checked) {
        return '<label style="height: 22px">' +
            '<input class="ace ace-switch ace-switch-6" '+(checked ? 'checked':'')+' type="checkbox"/>' +
            '<span class="lbl"></span>' +
            '</label>'
        return html
    }

    $$.ajax = function (url, data, options) {
    	var defaults = {
    		method: 'get',
    		dataType: 'json',
    		error: function(xhr, status, error) {
                if (status==='error')
                    ace.alert.err('服务器错误', {icon: 2});
            },
            success: function(res) {
    			if (res.code == 0) {
    				ace.alert.ok(res.msg, function() {
    					if (typeof refreshTable === "function") {
    						refreshTable()
    					}
    				})
    			} else {
    				ace.alert.err(res.msg)
    			}
    		},
    	}
    	var options = $.extend({}, defaults, options)
    	options.url = url
    	options.data = data
    	$.ajax(options)
    }

    $$.tableEdit = function (field, row, index, oldValue, ele, editUrl, table) {
        if (typeof editUrl === 'undefined') editUrl = 'edit'
        ace.helper.ajax(editUrl, {id:row.id, [field]:row[field]}, {
        	method: 'POST',
        	success: function(res) {
        		if (res.code != 0) {
        			table.bootstrapTable('updateCell', {
		                index: index,
		                field: field,
		                value: oldValue
		            })
        		}
        	}
        })
    }

})(ace.helper);;

//some form functions
(function ($$, undefined) {
    $$.serialize = function (formEle) {
    	var form = $(formEle)
        var serializeObj={}
        var array = form.serializeArray()
        $(array).each(function(){
            if(serializeObj[this.name]){
                if($.isArray(serializeObj[this.name])){
                    serializeObj[this.name].push(this.value);
                }else{
                    serializeObj[this.name]=[serializeObj[this.name],this.value];
                }
            }else{
                serializeObj[this.name]=this.value;
            }
        });
        return serializeObj
    }
    $$.showErrors = function (errors) {
		for (let key in errors) {
	        ace.form.renderError($('<div class="help-block error">' + errors[key][0] + '</div>'), $('[name=' + key + ']'))
        }
    }
    $$.renderError = function (error, ele) {
    	var $fromGroup = ele.closest('.form-group')
		if ($fromGroup.find('.error-box').length > 0) {
			$fromGroup.find('.error-box').html(error)
		} else {
			error.appendTo($fromGroup.children('div'))
		}
    }
    $$.ajaxSub = function (form, okCb) {
    	$(form).submit(function() {
    		$(form).ajaxSubmit({
				dataType : "json",
				success: function(res) {
					if(res.code==0) {
						ace.alert.ok(res.msg, function(){
							if (typeof okCb === 'function') {
								okCb()
							}
						})
					}else{
						ace.form.showErrors(res.data)
						ace.alert.err(res.msg)
					}
				},
				error: function(xhr, status, error) {
					if(status==='error'){
						ace.alert.err('服务器错误')
					}
				}
			});
			return false;
    	})
    }
})(ace.form);

//some alert functions
(function ($$, undefined) {//$$ is ace.match
    $$.ok = function (msg, cb) {
        layer.msg(msg, {icon: 1}, function() {
            if (typeof cb === 'function') {
                cb()
            }
        });
    }
    $$.err = function (msg, cb) {
        layer.msg(msg, {icon: 2}, function() {
            if (typeof cb === 'function') {
                cb()
            }
        });
    }
    $$.confirm = function (msg, cb) {
        layer.confirm(msg, {icon: 3}, function(){
            if (typeof cb === 'function') cb()
        })
    }
})(ace.alert);

//some match functions
(function ($$, undefined) {//$$ is ace.match
    $$.int = function (v, type) {
        if (type == 0 || type == 'undefined') {
            return /(^0$)|(^[1-9]\d*$)|(^-[1-9]\d*$)/.test(v)
        } else if (type == 1) {
            return /(^0$)|(^[1-9]\d*$)/.test(v)
        } else if (type == -1) {
            return /^-[1-9]\d*$/.test(v)
        } else if (type == 2) {
            return /^[1-9]\d*$/.test(v)
        }
    }
    $$.money = function (v) {
        return /(^0$)|(^[1-9]\d*$)|(^0\.\d{1,2}$)|(^[1-9]\d*\.\d{1,2}$)/.test(v)
    }
})(ace.match);

/**
 <b>Ace demo functions</b>. Calls sidebar functions, demo ajax, some fixes, etc
*/
//document ready function
jQuery(function($) {
  try {
	ace.handles.init();
  } catch(e) {}
});

(function($ , undefined) {
	ace.handles = {
		functions: {},
		init: function(initAnyway) {
			for(var func in ace.handles.functions) if(ace.handles.functions.hasOwnProperty(func)) {
				ace.handles.functions[func]();
			}
		}
	}

	ace.handles.functions.basics = function() {
		if(ace.vars['non_auto_fixed']) {
			$('body').addClass('mob-safari');
		}
		ace.vars['transition'] = ace.vars['animation'] || !!$.support.transition;
	}

	ace.handles.functions.datePicker = function() {
		$('.date-picker').datepicker({
			language: 'zh-CN',
			format:'yyyy-mm-dd',
			autoclose:true,
			todayHighlight: true
		});
	}

	ace.handles.functions.dateTimepicker = function() {
		// $('.date-timepicker').datepicker({
		// 	language: 'zh-CN',
		// 	format:'yyyy-mm-dd HH:ii:ss',
		// 	autoclose:true,
		// 	todayHighlight: true
		// });

		$('.date-timepicker').datetimepicker({
			format: 'MM/DD/YYYY h:mm:ss A',//use this option to display seconds
			icons: {
				time: 'fa fa-clock-o',
				date: 'fa fa-calendar',
				up: 'fa fa-chevron-up',
				down: 'fa fa-chevron-down',
				previous: 'fa fa-chevron-left',
				next: 'fa fa-chevron-right',
				today: 'fa fa-arrows ',
				clear: 'fa fa-trash',
				close: 'fa fa-times'
			}
		}).next().on(ace.click_event, function(){
			$(this).prev().focus();
		});
	}

	ace.handles.functions.tableAction = function() {
		$('body').on('click', 'table .actions [action]', function(){
			var action = $(this).attr('action')
            var data = $(this).data()
			var tr = $(this).closest('tr')
            data.id = tr.attr('unique-id')
            var url = $(this).attr('url')

            if ($(this).attr('confirm') !== 'undefined') {
                var title = $(this).attr('title')
                if (!title) {
                	title = $(this).text()
                }
                layer.confirm('确定要'+title+'吗？', {icon: 3}, function() {
                	ace.helper.ajax(url, data)
                })
            } else {
            	ace.helper.ajax(url, data)
            }
		})
	}

	ace.handles.functions.formModal = function() {
		$('body').on('click', '.form-modal', function(){
        	ace.helper.ajax($(this).attr('href'), $(this).data(), {
        		success: function (res) {
        			$('body').append(res)
            		$('#itemFormModal').modal('show')
        		}
        	})
			return false;
		})
	}

	ace.handles.functions.itemFormModal = function() {
		$(document)
        .on('hide.bs.modal', '#itemFormModal', function(e) {
            $(this).remove();
        })
        .on('show.bs.modal', '#itemFormModal', function(e) {
            var that = $(this)
            ace.form.ajaxSub('#saveForm', function() {
                refreshTable()
                that.remove()
            })
        })
	}
	ace.handles.functions.enableRegionPicker = function() {
		$('.region-picker').each(function() {
			var that = $(this)
			if (that.hasClass('province')) {
				$.getJSON('/site/regions', function(data){
					var val = that.data('val')
					var html = ''
					$.each(data, function(k, v) {
						html += '<option value="'+ k +'" '+(val==k ? 'selected' : '')+'>' + v
					})
					that.append(html)
					if (val > 0) {
						$.getJSON('/site/regions?pid='+val, function(data){
							var val2 = that.next().data('val')
							var html = ''
							$.each(data, function(k, v) {
								html += '<option value="'+ k +'" '+(val2==k ? 'selected' : '')+'>' + v
							})
							that.next().append(html)
							if (val2 > 0) {
								$.getJSON('/site/regions?pid='+val2, function(data){
									var val3 = that.data('val')
									var html = ''
									$.each(data, function(k, v) {
										html += '<option value="'+ k +'" '+(val3==k ? 'selected' : '')+'>' + v
									})
									that.next().next().append(html)
								})
							}
						})
					}
				})
			}
		})
		$('body').on('change', '.region-picker', function() {
			var that = $(this)
			that.nextAll('select').each(function(){
				$(this).children('option').not(':first').remove()
			})
			var pid = that.val()
			if (pid > 0 && !$(this).hasClass('county')) {
				$.getJSON('/site/regions?pid=' + pid, function(data){
					var html = ''
					$.each(data, function(k, v) {
						html += '<option value="'+ k +'">' + v
					})
					that.next('select').append(html)
				})
			}
		})
	}

	ace.handles.functions.enableSidebar = function() {
		//initiate sidebar function
		var $sidebar = $('.sidebar');
		if($.fn.ace_sidebar) $sidebar.ace_sidebar();
		if($.fn.ace_sidebar_scroll) $sidebar.ace_sidebar_scroll({
			//for other options please see documentation
			'include_toggle': false || ace.vars['safari'] || ace.vars['ios_safari'] //true = include toggle button in the scrollbars
		});

		$('.nav.nav-list .active').parents('li').addClass('active open')

		$('body').on('click', '.reloadMenuBtn', function () {
        	$.getJSON('/site/reloadmenu', function(res) {
                window.location.reload()
            })
        })
	}

	/////////////////////////////
	ace.handles.functions.handleScrollbars = function() {
		//add scrollbars for navbar dropdowns
		var has_scroll = !!$.fn.ace_scroll;
		if(has_scroll) $('.dropdown-content').ace_scroll({reset: false, mouseWheelLock: true})

		//reset scrolls bars on window resize
		if(has_scroll && !ace.vars['old_ie']) {//IE has an issue with widget fullscreen on ajax?!!!
			$(window).on('resize.reset_scroll', function() {
				$('.ace-scroll:not(.scroll-disabled)').not(':hidden').ace_scroll('reset');
			});
			if(has_scroll) $(document).on('settings.ace.reset_scroll', function(e, name) {
				if(name == 'sidebar_collapsed') $('.ace-scroll:not(.scroll-disabled)').not(':hidden').ace_scroll('reset');
			});
		}
	}

	ace.handles.functions.dropdownAutoPos = function() {
		//change a dropdown to "dropup" depending on its position
		$(document).on('click.dropdown.pos', '.dropdown-toggle[data-position="auto"]', function() {
			var offset = $(this).offset();
			var parent = $(this.parentNode);

			if ( parseInt(offset.top + $(this).height()) + 50 
					>
				(ace.helper.scrollTop() + ace.helper.winHeight() - parent.find('.dropdown-menu').eq(0).height()) 
				) parent.addClass('dropup');
			else parent.removeClass('dropup');
		});
	}
})(jQuery);;/**
 <b>Scroll to top button</b>.
*/
(function($ , undefined) {
	var scroll_btn = $('.btn-scroll-up');
	if(scroll_btn.length > 0) {
		var is_visible = false;
		$(window).on('scroll.scroll_btn', function() {
			var scroll = ace.helper.scrollTop();
			var h = ace.helper.winHeight();
			var body_sH = document.body.scrollHeight;
			if(scroll > parseInt(h / 4) || (scroll > 0 && body_sH >= h && h + scroll >= body_sH - 1)) {//|| for smaller pages, when reached end of page
				if(!is_visible) {
					scroll_btn.addClass('display');
					is_visible = true;
				}
			} else {
				if(is_visible) {
					scroll_btn.removeClass('display');
					is_visible = false;
				}
			}
		}).triggerHandler('scroll.scroll_btn');

		scroll_btn.on(ace.click_event, function(){
			var duration = Math.min(500, Math.max(100, parseInt(ace.helper.scrollTop() / 3)));
			$('html,body').animate({scrollTop: 0}, duration);
			return false;
		});
	}
})(window.jQuery);;/**
 <b>Sidebar functions</b>. Collapsing/expanding and other sidebar functions.
*/
(function($ , undefined) {
	function Sidebar(sidebar, settings) {
		var self = this;
		this.$sidebar = $(sidebar);
		this.$sidebar.attr('data-sidebar', 'true');

		this.settings = $.extend({}, $.fn.ace_sidebar.defaults, settings);

		//some vars
		this.minimized = false;//will be initialized later

		//return an array containing sidebar state variables
		this.vars = function() {
			return {'minimized': this.minimized}
		}
		this.get = function(name) {
			if(this.hasOwnProperty(name)) return this[name];
		}
		this.set = function(name, value) {
			if(this.hasOwnProperty(name)) this[name] = value;
		}

		//return a reference to self (sidebar instance)
		this.ref = function() {
			return this;
		}
		
		//toggle icon for sidebar collapse/expand button
		var toggleIcon = function(minimized, save) {
			var icon = $(this).find(ace.vars['.icon']), icon1, icon2;
			if(icon.length > 0) {
				icon1 = icon.attr('data-icon1');//the icon for expanded state
				icon2 = icon.attr('data-icon2');//the icon for collapsed state

				if(typeof minimized !== "undefined") {
					if(minimized) icon.removeClass(icon1).addClass(icon2);
					else icon.removeClass(icon2).addClass(icon1);
				} else {
					icon.toggleClass(icon1).toggleClass(icon2);
				}
				
				try {
					if(save !== false) ace.settings.saveState(icon.get(0));
				} catch(e) {}
			}
		}
		
		//if not specified, find the toggle button related to this sidebar
		var findToggleBtn = function() {
			var toggle_btn = self.$sidebar.find('.sidebar-collapse');
			if(toggle_btn.length == 0) toggle_btn = $('.sidebar-collapse[data-target="#'+(self.$sidebar.attr('id')||'')+'"]');
			if(toggle_btn.length != 0) toggle_btn = toggle_btn[0];
			else toggle_btn = null;
			
			return toggle_btn;
		}
		
		//collapse/expand sidebar
		this.toggleMenu = function(toggle_btn, save) {
			this.minimized = !this.minimized;
			var save = !(toggle_btn === false || save === false);
			
			if(this.minimized) this.$sidebar.addClass('menu-min');
			else this.$sidebar.removeClass('menu-min');

			try {
				if(save) ace.settings.saveState(sidebar, 'class', 'menu-min', this.minimized);
			} catch(e) {}
		
			if( !toggle_btn ) {
				toggle_btn = findToggleBtn();
			}
			if(toggle_btn) {
				toggleIcon.call(toggle_btn, this.minimized, save);
			}
			this.$sidebar.trigger($.Event((this.minimized ? 'collapse' : 'expand')+'.ace.sidebar'));

			return true;
		}
		this.collapse = function(toggle_btn, save) {
			this.minimized = false;
			this.toggleMenu(toggle_btn, save) 
		}
		this.expand = function(toggle_btn, save) {
			this.minimized = true;
			this.toggleMenu(toggle_btn, save);
		}
		
		//some helper functions
		
		var submenu_working = false;
		//show submenu
		this.show = function(sub, $duration, shouldWait) {
			//'shouldWait' indicates whether to wait for previous transition (submenu toggle) to be complete or not?
			shouldWait = (shouldWait !== false);
			if(shouldWait && submenu_working) return false;
					
			var $sub = $(sub);
			var event;
			$sub.trigger(event = $.Event('show.ace.submenu'))
			if (event.isDefaultPrevented()) {
				return false;
			}
			
			if(shouldWait) submenu_working = true;

			$duration = typeof $duration !== 'undefined' ? $duration : this.settings.duration;
			
			$sub.css({
				height: 0,
				overflow: 'hidden',
				display: 'block'
			}).removeClass('nav-hide').addClass('nav-show').parent().addClass('open');
			
			sub.scrollTop = 0;//this is for submenu_hover when sidebar is minimized and a submenu is scrollTop'ed using scrollbars ...

			var complete = function(ev, trigger) {
				ev && ev.stopPropagation();
				$sub
				.css({'transition-property': '', 'transition-duration': '', overflow:'', height: ''})
				//if(ace.vars['webkit']) ace.helper.redraw(sub);//little Chrome issue, force redraw ;)

				if(trigger !== false) $sub.trigger($.Event('shown.ace.submenu'))
				
				if(shouldWait) submenu_working = false;
			}
			
			var finalHeight = sub.scrollHeight;

			if($duration == 0 || finalHeight == 0 || !$.support.transition.end) {
				//(if duration is zero || element is hidden (scrollHeight == 0) || CSS3 transitions are not available)
				complete();
			} else {
				$sub.css({
					 'height': finalHeight,
					 'transition-property': 'height',
					 'transition-duration': ($duration/1000)+'s'
					}
				).one($.support.transition.end, complete);
				
				//there is sometimes a glitch, so maybe retry
				if(ace.vars['android'] ) {
					setTimeout(function() {
						complete(null, false);
						ace.helper.redraw(sub);
					}, $duration + 20);
				}
			}
			return true;
		 }
		 
		 //hide submenu
		 this.hide = function(sub, $duration, shouldWait) {
			//'shouldWait' indicates whether to wait for previous transition (submenu toggle) to be complete or not?
			shouldWait = (shouldWait !== false);
			if(shouldWait && submenu_working) return false;
		 
			var $sub = $(sub);
			var event;
			$sub.trigger(event = $.Event('hide.ace.submenu'))
			if (event.isDefaultPrevented()) {
				return false;
			}
			
			if(shouldWait) submenu_working = true;
			
			$duration = typeof $duration !== 'undefined' ? $duration : this.settings.duration;
			
			var initialHeight = sub.scrollHeight;
			$sub.css({
				height: initialHeight,
				overflow: 'hidden',
				display: 'block'
			}).parent().removeClass('open');

			sub.offsetHeight;
			//forces the "sub" to re-consider the new 'height' before transition
			
			var complete = function(ev, trigger) {
				ev && ev.stopPropagation();
				$sub
				.css({display: 'none', overflow:'', height: '', 'transition-property': '', 'transition-duration': ''})
				.removeClass('nav-show').addClass('nav-hide')

				if(trigger !== false) $sub.trigger($.Event('hidden.ace.submenu'))
				
				if(shouldWait) submenu_working = false;
			}
			
			if( $duration == 0 || initialHeight == 0 || !$.support.transition.end) {
				//(if duration is zero || element is hidden (scrollHeight == 0) || CSS3 transitions are not available)
				complete();
			} else {
				$sub.css({
					 'height': 0,
					 'transition-property': 'height',
					 'transition-duration': ($duration/1000)+'s'
					}
				).one($.support.transition.end, complete);
				
				//there is sometimes a glitch, so maybe retry
				if(ace.vars['android'] ) {
					setTimeout(function() {
						complete(null, false);
						ace.helper.redraw(sub);
					}, $duration + 20);
				}
			}
			return true;
		 }

		 //toggle submenu
		 this.toggle = function(sub, $duration) {
			$duration = $duration || self.settings.duration;
		 
			if( sub.scrollHeight == 0 ) {//if an element is hidden scrollHeight becomes 0
				if( this.show(sub, $duration) ) return 1;
			} else {
				if( this.hide(sub, $duration) ) return -1;
			}
			return 0;
		 }
		
		////////////////
		//sidebar vars
		var minimized_menu_class  = 'menu-min';

		function update_vars() {
			this.minimized = this.$sidebar.hasClass(minimized_menu_class)
		}

		//update some basic variables
		$(window).on('resize.sidebar.vars' , function(){
			update_vars.call(self);
		}).triggerHandler('resize.sidebar.vars')
		
		
		//toggling (show/hide) submenu elements
		this.$sidebar.on(ace.click_event+'.ace.submenu', '.nav-list', function (ev) {
			var nav_list = this;

			//check to see if we have clicked on an element which is inside a .dropdown-toggle element?!
			//if so, it means we should toggle a submenu
			var link_element = $(ev.target).closest('a');
			if(!link_element || link_element.length == 0) return;//return if not clicked inside a link element

			var minimized  = self.minimized;
			//if .sidebar is .navbar-collapse and in small device mode, then let minimized be uneffective
	
			if( !link_element.hasClass('dropdown-toggle') ) {//it doesn't have a submenu return
				if( ace.click_event == 'tap' && minimized &&
					link_element.get(0).parentNode.parentNode == nav_list )//only level-1 links
				{
					var text = link_element.find('.menu-text').get(0);
					if( text != null && ev.target != text && !$.contains(text , ev.target) ) {//not clicking on the text or its children
						ev.preventDefault();
						return false;
					}
				}
				//ios safari only has a bit of a problem not navigating to link address when scrolling down
				//specify data-link attribute to ignore this
				if(ace.vars['ios_safari'] && link_element.attr('data-link') !== 'false') {
					//only ios safari has a bit of a problem not navigating to link address when scrolling down
					//please see issues section in documentation
					document.location = link_element.attr('href');
					ev.preventDefault();
					return false;
				}
				return;
			}
			
			ev.preventDefault();
			
			var sub = link_element.siblings('.submenu').get(0);
			if(!sub) return false;
			var $sub = $(sub);

			var height_change = 0;//the amount of height change in .nav-list

			var parent_ul = sub.parentNode.parentNode;
			if (
				( minimized && parent_ul == nav_list )
				 || 
				( ( $sub.parent().hasClass('hover') && $sub.css('position') == 'absolute' ))
			) {
				return false;
			}

			var sub_hidden = (sub.scrollHeight == 0)
			//if not open and visible, let's open it and make it visible
			if( sub_hidden && self.settings.hide_open_subs ) {//being shown now
			  $(parent_ul).find('> .open > .submenu').each(function() {
				//close all other open submenus except for the active one
				if(this != sub && !$(this.parentNode).hasClass('active')) {
					height_change -= this.scrollHeight;
					self.hide(this, self.settings.duration, false);
				}
			  })
			}

			if( sub_hidden ) {//being shown now
				self.show(sub, self.settings.duration);
				//if a submenu is being shown and another one previously started to hide, then we may need to update/hide scrollbars
				//but if no previous submenu is being hidden, then no need to check if we need to hide the scrollbars in advance
				if(height_change != 0) height_change += sub.scrollHeight;//we need new updated 'scrollHeight' here
			} else {
				self.hide(sub, self.settings.duration);
				height_change -= sub.scrollHeight;
				//== -1 means submenu is being hidden
			}

			//hide scrollbars if content is going to be small enough that scrollbars is not needed anymore
			if (height_change != 0) {
				if(self.$sidebar.attr('data-sidebar-scroll') == 'true' && !self.minimized) 
					self.$sidebar.ace_sidebar_scroll('prehide', height_change)
			}
			return false;
		});
	}//end of Sidebar
	
	//sidebar collapse/expand button
	$(document).on(ace.click_event+'.ace.menu', '.sidebar-collapse', function(e){
		var target = $(this).attr('data-target'), $sidebar = null;
		if(target) $sidebar = $(target);
		if($sidebar == null || $sidebar.length == 0) $sidebar = $(this).closest('.sidebar');
		if($sidebar.length == 0) return;

		e.preventDefault();
		$sidebar.ace_sidebar('toggleMenu', this);
	})
	
	$.fn.ace_sidebar = function (option, value, value2) {
		var method_call;

		var $set = this.each(function () {
			var $this = $(this);
			var data = $this.data('ace_sidebar');
			var options = typeof option === 'object' && option;

			if (!data) $this.data('ace_sidebar', (data = new Sidebar(this, options)));
			if (typeof option === 'string' && typeof data[option] === 'function') {
				if(value instanceof Array) method_call = data[option].apply(data, value);
				else if(value2 !== undefined) method_call = data[option](value, value2);
				else method_call = data[option](value);
			}
		});

		return (method_call === undefined) ? $set : method_call;
	};
	
	$.fn.ace_sidebar.defaults = {
		'duration': 300,
		'hide_open_subs': true
    }
})(window.jQuery);;/**
 <b>Scrollbars for sidebar</b>
*/
(function($ , undefined) {
	var old_safari = ace.vars['safari'] && navigator.userAgent.match(/version\/[1-5]/i)
	//NOTE
	//Safari on windows has not been updated for a long time.
	//And it has a problem when sidebar is fixed & scrollable and there is a CSS3 animation inside page content.
	//Very probably windows users of safari have migrated to another browser by now!

	var is_element_pos =
	'getComputedStyle' in window ?
	//el.offsetHeight is used to force redraw and recalculate 'el.style.position' esp. for webkit!
	function(el, pos) { el.offsetHeight; return window.getComputedStyle(el).position == pos }
	:
	function(el, pos) { el.offsetHeight; return $(el).css('position') == pos }
	
		
	function Sidebar_Scroll(sidebar , settings) {
		var self = this;

		var $window = $(window);
		
		var $sidebar = $(sidebar), $nav, nav, $toggle, $shortcuts;
		$nav = $sidebar.find('.nav-list');
		nav = $nav.get(0);
		if(!nav) return;
		
		
		var attrib_values = ace.helper.getAttrSettings(sidebar, $.fn.ace_sidebar_scroll.defaults);
		this.settings = $.extend({}, $.fn.ace_sidebar_scroll.defaults, settings, attrib_values);
		var scroll_to_active = self.settings.scroll_to_active;
	
	
		var ace_sidebar = $sidebar.ace_sidebar('ref');
		$sidebar.attr('data-sidebar-scroll', 'true');
			
		
		var scroll_div = null,
			scroll_content = null,
			scroll_content_div = null,
			bar = null,
			track = null,
			ace_scroll = null;

		this.is_scrolling = false;
		var _initiated = false;
		this.sidebar_fixed = is_element_pos(sidebar, 'fixed');
		
		var $avail_height, $content_height;
		
		var available_height = function() {
			//available window space
			var offset = $nav.parent().offset();//because `$nav.offset()` considers the "scrolled top" amount as well
			if(self.sidebar_fixed) offset.top -= ace.helper.scrollTop();

			return $window.innerHeight() - offset.top - ( self.settings.include_toggle ? 0 : $toggle.outerHeight() ) + 1;
		}
		var content_height = function() {
			return nav.clientHeight;//we don't use nav.scrollHeight here, because hover submenus are considered in calculating scrollHeight despite position=absolute!
		}
		
		var initiate = function(on_page_load) {
			if( _initiated ) return;
			if( !self.sidebar_fixed ) return;//eligible??
			//return if we want scrollbars only on "fixed" sidebar and sidebar is not "fixed" yet!

			
			$nav = $sidebar.find('.nav-list');
			$toggle = $sidebar.find('.sidebar-toggle').eq(0);
			$shortcuts = $sidebar.find('.sidebar-shortcuts').eq(0);
			nav = $nav.get(0);

			if(!nav) return;
			
			//initiate once
			$nav.wrap('<div class="nav-wrap-up pos-rel" />');
			$nav.after('<div><div></div></div>');

			$nav.wrap('<div class="nav-wrap" />');
			
			if(!self.settings.include_toggle) $toggle.css({'z-index': 1});
			if(!self.settings.include_shortcuts) $shortcuts.css({'z-index': 99});

			scroll_div = $nav.parent().next()
			.ace_scroll({
				size: available_height(),
				//reset: true,
				mouseWheelLock: true,
				hoverReset: false,
				dragEvent: true,
				styleClass: self.settings.scroll_style,
				touchDrag: false//disable touch drag event on scrollbars, we'll add a custom one later
			})
			.closest('.ace-scroll').addClass('nav-scroll');
			
			ace_scroll = scroll_div.data('ace_scroll');

			scroll_content = scroll_div.find('.scroll-content').eq(0);
			scroll_content_div = scroll_content.find(' > div').eq(0);
			
			track = $(ace_scroll.get_track());
			bar = track.find('.scroll-bar').eq(0);

			if(self.settings.include_shortcuts && $shortcuts.length != 0) {
				$nav.parent().prepend($shortcuts).wrapInner('<div />');
				$nav = $nav.parent();
			}
			if(self.settings.include_toggle && $toggle.length != 0) {
				$nav.append($toggle);
				$nav.closest('.nav-wrap').addClass('nav-wrap-t');//it just helps to remove toggle button's top border and restore li:last-child's bottom border
			}

			$nav.css({position: 'relative'});
			if( self.settings.scroll_outside == true ) scroll_div.addClass('scrollout');
			
			nav = $nav.get(0);
			nav.style.top = 0;
			scroll_content.on('scroll.nav', function() {
				nav.style.top = (-1 * this.scrollTop) + 'px';
			});
			
			//mousewheel library available?
			$nav.on(!!$.event.special.mousewheel ? 'mousewheel.ace_scroll' : 'mousewheel.ace_scroll DOMMouseScroll.ace_scroll', function(event){
				if( !self.is_scrolling || !ace_scroll.is_active() ) {
					return !self.settings.lock_anyway;
				}
				//transfer $nav's mousewheel event to scrollbars
				return scroll_div.trigger(event);
			});
			
			$nav.on('mouseenter.ace_scroll', function() {
				track.addClass('scroll-hover');
			}).on('mouseleave.ace_scroll', function() {
				track.removeClass('scroll-hover');
			});

			//you can also use swipe event in a similar way //swipe.nav
			var content = scroll_content.get(0);
			$nav.on('ace_drag.nav', function(event) {
				if( !self.is_scrolling || !ace_scroll.is_active() ) {
					event.retval.cancel = true;
					return;
				}
				
				//if submenu hover is being scrolled let's cancel sidebar scroll!
				if( $(event.target).closest('.can-scroll').length != 0 ) {
					event.retval.cancel = true;
					return;
				}

				if(event.direction == 'up' || event.direction == 'down') {
					ace_scroll.move_bar(true);
					
					var distance = event.dy;
					
					distance = parseInt(Math.min($avail_height, distance))
					if(Math.abs(distance) > 2) distance = distance * 2;
					
					if(distance != 0) {
						content.scrollTop = content.scrollTop + distance;
						nav.style.top = (-1 * content.scrollTop) + 'px';
					}
				}
			});

			//for drag only
			if(self.settings.smooth_scroll) {
				$nav
				.on('touchstart.nav MSPointerDown.nav pointerdown.nav', function(event) {
					$nav.css('transition-property', 'none');
					bar.css('transition-property', 'none');
				})
				.on('touchend.nav touchcancel.nav MSPointerUp.nav MSPointerCancel.nav pointerup.nav pointercancel.nav', function(event) {
					$nav.css('transition-property', 'top');
					bar.css('transition-property', 'top');
				});
			}
			
			if(old_safari && !self.settings.include_toggle) {
				var toggle = $toggle.get(0);
				if(toggle) scroll_content.on('scroll.safari', function() {
					ace.helper.redraw(toggle);
				});
			}

			_initiated = true;

			//if the active item is not visible, scroll down so that it becomes visible
			//only the first time, on page load
			if(on_page_load == true) {
				self.reset();//try resetting at first

				if( scroll_to_active ) {
					self.scroll_to_active();
				}
				scroll_to_active = false;
			}
			
			if( typeof self.settings.smooth_scroll === 'number' && self.settings.smooth_scroll > 0) {
				$nav.css({'transition-property': 'top', 'transition-duration': (self.settings.smooth_scroll / 1000).toFixed(2)+'s'})
				bar.css({'transition-property': 'top', 'transition-duration': (self.settings.smooth_scroll / 1500).toFixed(2)+'s'})
				
				scroll_div
				.on('drag.start', function(e) {
					e.stopPropagation();
					$nav.css('transition-property', 'none')
				})
				.on('drag.end', function(e) {
					e.stopPropagation();
					$nav.css('transition-property', 'top')
				});
			}
			
			if(ace.vars['android']) {
				//force hide address bar, because its changes don't trigger window resize and become kinda ugly
				var val = ace.helper.scrollTop();
				if(val < 2) {
					window.scrollTo( val, 0 );
					setTimeout( function() {
						self.reset();
					}, 20 );
				}
				
				var last_height = ace.helper.winHeight() , new_height;
				$(window).on('scroll.ace_scroll', function() {
					if(self.is_scrolling && ace_scroll.is_active()) {
						new_height = ace.helper.winHeight();
						if(new_height != last_height) {
							last_height = new_height;
							self.reset();
						}
					}
				});
			}
		}
		
		this.scroll_to_active = function() {
			if( !ace_scroll || !ace_scroll.is_active() ) return;
			try {
				//sometimes there's no active item or not 'offsetTop' property
				var $active;
				
				var vars = ace_sidebar['vars']()

				var nav_list = $sidebar.find('.nav-list')
				if(vars['minimized'] && !vars['collapsible']) {
					$active = nav_list.find('> .active')
				}
				else {
					$active = $nav.find('> .active.hover')
					if($active.length == 0)	$active = $nav.find('.active:not(.open)')
				}

				var top = $active.outerHeight();
				nav_list = nav_list.get(0);
				var active = $active.get(0);
				while(active != nav_list) {
					top += active.offsetTop;
					active = active.parentNode;
				}

				var scroll_amount = top - scroll_div.height();
				if(scroll_amount > 0) {
					nav.style.top = -scroll_amount + 'px';
					scroll_content.scrollTop(scroll_amount);
				}
			}catch(e){}
		}
		
		this.reset = function(recalc) {
			if(recalc === true) {
				this.sidebar_fixed = is_element_pos(sidebar, 'fixed');
			}
			
			if( !this.sidebar_fixed ) {
				this.disable();
				return;//eligible??
			}

			//return if we want scrollbars only on "fixed" sidebar and sidebar is not "fixed" yet!

			if( !_initiated ) initiate();
			//initiate scrollbars if not yet
			
			var vars = ace_sidebar['vars']();

			var enable_scroll = !vars['collapsible'] && !vars['horizontal']
								&& ($avail_height = available_height()) < ($content_height = nav.clientHeight);
								//we don't use nav.scrollHeight here, because hover submenus are considered in calculating scrollHeight despite position=absolute!
								
			this.is_scrolling = true;
			if( enable_scroll ) {
				scroll_content_div.css({height: $content_height, width: 8});
				scroll_div.prev().css({'max-height' : $avail_height})
				ace_scroll.update({size: $avail_height})
				ace_scroll.enable();
				ace_scroll.reset();
			}
			if( !enable_scroll || !ace_scroll.is_active() ) {
				if(this.is_scrolling) this.disable();
			} else {
				$sidebar.addClass('sidebar-scroll');
			}
			
			//return this.is_scrolling;
		}
		
		this.disable = function() {
			this.is_scrolling = false;
			if(scroll_div) {
				scroll_div.css({'height' : '', 'max-height' : ''});
				scroll_content_div.css({height: '', width: ''});//otherwise it will have height and takes up some space even when invisible
				scroll_div.prev().css({'max-height' : ''})
				ace_scroll.disable();
			}

			if(parseInt(nav.style.top) < 0 && self.settings.smooth_scroll && $.support.transition.end) {
				$nav.one($.support.transition.end, function() {
					$sidebar.removeClass('sidebar-scroll');
					$nav.off('.trans');
				});
			} else {
				$sidebar.removeClass('sidebar-scroll');
			}

			nav.style.top = 0;
		}
		
		this.prehide = function(height_change) {
			if(!this.is_scrolling || ace_sidebar.get('minimized')) return;//when minimized submenu's toggle should have no effect
			
			if(content_height() + height_change < available_height()) {
				this.disable();
			}
			else if(height_change < 0) {
				//if content height is decreasing
				//let's move nav down while a submenu is being hidden
				var scroll_top = scroll_content.scrollTop() + height_change
				if(scroll_top < 0) return;

				nav.style.top = (-1 * scroll_top) + 'px';
			}
		}
		
		
		this._reset = function(recalc) {
			if(recalc === true) {
				this.sidebar_fixed = is_element_pos(sidebar, 'fixed');
			}
			
			if(ace.vars['webkit']) 
				setTimeout(function() { self.reset() } , 0);
			else this.reset();
		}
		
		
		this.set_hover = function() {
			if(track) track.addClass('scroll-hover');
		}
		
		this.get = function(name) {
			if(this.hasOwnProperty(name)) return this[name];
		}
		this.set = function(name, value) {
			if(this.hasOwnProperty(name)) this[name] = value;
		}
		this.ref = function() {
			//return a reference to self
			return this;
		}
		
		this.updateStyle = function(styleClass) {
			if(ace_scroll == null) return;
			ace_scroll.update({styleClass: styleClass});
		}

		
		//change scrollbar size after a submenu is hidden/shown
		//but don't change if sidebar is minimized
		$sidebar.on('hidden.ace.submenu.sidebar_scroll shown.ace.submenu.sidebar_scroll', '.submenu', function(e) {
			e.stopPropagation();

			if( !ace_sidebar.get('minimized') ) {
				//webkit has a little bit of a glitch!!!
				self._reset();
				if( e.type == 'shown' ) self.set_hover();
			}
		});

		
		initiate(true);//true = on_page_load
	}
	
	//reset on document and window changes
	$(document).on('settings.ace.sidebar_scroll', function(ev, event_name, event_val){
		$('.sidebar[data-sidebar-scroll=true]').each(function() {
			var $this = $(this);
			var sidebar_scroll = $this.ace_sidebar_scroll('ref');

			if( event_name == 'sidebar_collapsed' && is_element_pos(this, 'fixed') ) {
				sidebar_scroll._reset();
			}
			else if( event_name === 'sidebar_fixed' || event_name === 'navbar_fixed' ) {
				var is_scrolling = sidebar_scroll.get('is_scrolling');
				var sidebar_fixed = is_element_pos(this, 'fixed')
				sidebar_scroll.set('sidebar_fixed', sidebar_fixed);

				if(sidebar_fixed && !is_scrolling) {
					sidebar_scroll._reset();
				}
				else if( !sidebar_fixed ) {
					sidebar_scroll.disable();
				}
			}
		});
	});
	
	$(window).on('resize.ace.sidebar_scroll', function(){
		$('.sidebar[data-sidebar-scroll=true]').each(function() {
			var $this = $(this);
			/////////////
			var sidebar_scroll = $(this).ace_sidebar_scroll('ref');
			
			var sidebar_fixed = is_element_pos(this, 'fixed')
			sidebar_scroll.set('sidebar_fixed', sidebar_fixed);
			sidebar_scroll._reset();
		});
	})
	
	 /////////////////////////////////////////////
	if(!$.fn.ace_sidebar_scroll) {
	  
	  $.fn.ace_sidebar_scroll = function (option, value) {
		var method_call;

		var $set = this.each(function () {
			var $this = $(this);
			var data = $this.data('ace_sidebar_scroll');
			var options = typeof option === 'object' && option;

			if (!data) $this.data('ace_sidebar_scroll', (data = new Sidebar_Scroll(this, options)));
			if (typeof option === 'string' && typeof data[option] === 'function') {
				method_call = data[option](value);
			}
		});

		return (method_call === undefined) ? $set : method_call;
	 }
	 
	 $.fn.ace_sidebar_scroll.defaults = {
		'scroll_to_active': true,
		'include_shortcuts': true,
		'include_toggle': false,
		'smooth_scroll': 150,
		'scroll_outside': false,
		'scroll_style': '',
		'lock_anyway': false
     }
	}
})(window.jQuery);;
